<?php
// +-------------------------------------------------------------------
// | 
// +-------------------------------------------------------------------
// | Copyright (c) 2009-2016 All rights reserved.
// +-------------------------------------------------------------------
namespace Kcdns\Service\Cron;

/**
 * php 常驻进程
 */
class Autorun
{

    const RUN = 4;

    const SLEEP = 3;

    const DONE = 2;

    const STOP = 1;

    const INIT = 0;
    
    // 当前脚本ID
    protected static $id;
    
    // 锁目录
    protected static $lockDir = './Data/Cron/';
    
    // 锁文件
    protected static $lockFile;
    
    // 执行时间
    protected static $execTime = 0;
    
    // 执行次数
    protected static $loopCount = 0;
    
    // 锁文件句柄
    protected static $fp = null;
    
    // 配置
    protected static $config = array(
            
            // 任务名称
            'name' => 'test',
            
            // 执行方式 : 按间隔执行, 最小执行间隔(秒)
            'interval' => 5,
            
            // 执行次数上限
            'maxloop' => 10,
            
            // 休眠时间
            'sleep' => 1,
            
            // 执行任务回调
            'callback' => false
    );

    public static function start ($config = array())
    {
        $status = self::init($config);
        
        do
        {
            switch ($status)
            {
                // 休眠周期
                case self::SLEEP:
                    sleep(self::$config['sleep']);
                    break;
                
                // 工作周期
                case self::RUN:
                    self::loop();
                    break;
                
                // 任务完成
                case self::DONE:
                    return self::clear();
                
                // 任务终止
                case self::STOP:
                    return false;
                
                default:
                    break;
            }
        }
        while ($status = self::checkLoop());
    }

    public static function clear ()
    {
        flock(self::$fp, LOCK_UN);
        fclose(self::$fp);
        unlink(self::$lockFile);
    }

    public static function errorClear ()
    {
        if ($e = error_get_last())
        {
            switch ($e['type'])
            {
                case E_ERROR:
                case E_PARSE:
                case E_CORE_ERROR:
                case E_COMPILE_ERROR:
                case E_USER_ERROR:
                    self::clear();
                    break;
            }
        }
    }

    public static function status ()
    {
        clearstatcache();
        $status = array();
        $locks = glob(self::$lockDir . '/*.lock');
        foreach ($locks as $_lock)
        {
            $fp = fopen($_lock, 'w');
            $_isRunning = $fp ? ! flock($fp, LOCK_EX | LOCK_NB) : true;
            $fp and flock($fp, LOCK_UN);
            $fp and fclose($fp);
            $status[substr(basename($_lock), 0, - 5)] = $_isRunning;
        }
        return $status;
    }

    protected static function init ($config)
    {
        ignore_user_abort(true);
        date_default_timezone_set('Asia/Chongqing');
        @set_time_limit(3600 * 24 * 30);
        
        is_dir(self::$lockDir) or mkdir(self::$lockDir, 0777, true);
        if (!file_exists(self::$lockDir))
        {
            self::log('创建目录失败[INIT], 退出');
            return self::STOP;
        }
        
        self::$config = array_merge(self::$config, $config);
        self::$id = date('Y-m-d-H-i-s') . '-' . uniqid();
        self::$lockFile = self::$lockDir . '/' . self::$config['name'] . '.lock';
        
        register_shutdown_function(array(
                __CLASS__,
                'errorClear'
        ));
        
        self::log('启动 : ' . json_encode(self::$config, JSON_UNESCAPED_UNICODE));
        
        // 初始化文件锁
        file_exists(self::$lockFile) or file_put_contents(self::$lockFile, self::$id);
        
        if (! self::$fp = fopen(self::$lockFile, 'w'))
        {
            self::log('开启锁文件失败[INIT], 退出');
            return self::STOP;
        }
        
        if (! flock(self::$fp, LOCK_EX | LOCK_NB))
        {
            self::log('获取文件锁失败[INIT], 退出');
            return self::STOP;
        }
        
        if (! fputs(self::$fp, self::$id))
        {
            self::log('写入文件锁失败[INIT], 退出');
            return self::STOP;
        }
        
        return self::INIT;
    }

    protected static function checkLoop ()
    {
        // 清除 filemtime 函数结果缓存
        clearstatcache();
        
        if (! file_exists(self::$lockFile))
        {
            self::log('文件锁丢失[CHECK], 退出');
            return self::STOP;
        }
        
        // 检查运行次数
        if (self::$loopCount >= self::$config['maxloop'])
        {
            self::log("当前进程运行次数达到上限 : " . self::$loopCount . " / " . self::$config['maxloop']);
            return self::DONE;
        }
        
        // 间隔执行 : 检查运行间隔
        if (time() - self::$execTime < self::$config['interval'])
        {
            return self::SLEEP;
        }
        
        // 可执行
        return self::RUN;
    }

    protected static function loop ()
    {
        self::$execTime = time();
        self::$loopCount ++;
        
        try
        {
            $res = is_callable(self::$config['callback'], true) ? call_user_func(self::$config['callback']) : '未指定 $config["callback"]';
        }
        catch (\Exception $e)
        {
            $res .= " EXCEPTION " . $e->getMessage();
        }
        
        self::log("执行回调 : " . json_encode($res, JSON_UNESCAPED_UNICODE));
    }

    protected static function log ($info = '', $specialLog = null)
    {
        function_exists('_log') and \KCSLog::DEBUG($info);
    }
}